import os
from typing import TextIO

from bean.Constants import Constants
from bean.TableInfo import TableInfo

from builder.CommentBuilder import CommentBuilder
from utils.StringUtils import get_last_suffix, uppercase_first_char


class MapperXmlBuilder:
    base_column_list = "base_column_list"
    base_query_condition = "base_query_condition"
    base_result_map = "base_result_map"
    base_query_condition_extend = "base_query_condition_extend"
    query_condition = "query_condition"

    @staticmethod
    def execute_build(table_info: TableInfo) -> None:
        if not os.path.exists(Constants.get_instance().MAPPERS_XML_PATH):
            os.makedirs(Constants.get_instance().MAPPERS_XML_PATH)
        with open(
                f"{Constants.get_instance().MAPPERS_XML_PATH}{table_info.bean_name}{Constants.get_instance().SUFFIX_MAPPERS}.xml",
                "w",
                encoding="utf-8"
        ) as f:
            MapperXmlBuilder.build_mapper_head(f, table_info)
            f.write("\n")
            MapperXmlBuilder.build_result_map(f, table_info)
            f.write("\n")
            MapperXmlBuilder.build_common_select_column(f, table_info)
            f.write("\n")
            MapperXmlBuilder.build_base_select_condition(f, table_info)
            f.write("\n")
            MapperXmlBuilder.build_extend_select_condition(f, table_info)
            f.write("\n")
            MapperXmlBuilder.connect_condition(f)
            f.write("\n")
            MapperXmlBuilder.build_select_list(f, table_info)
            f.write("\n")
            MapperXmlBuilder.build_select_count(f, table_info)
            f.write("\n")
            MapperXmlBuilder.build_insert(f, table_info, "insert")
            f.write("\n")
            MapperXmlBuilder.build_insert(f, table_info, "insertOrUpdate")
            f.write("\n")
            MapperXmlBuilder.build_insert_batch(f, table_info, "insertBatch")
            f.write("\n")
            MapperXmlBuilder.build_insert_batch(f, table_info, "insertOrUpdateBatch")
            f.write("\n")
            MapperXmlBuilder.build_fields_curd(f, table_info)
            f.write("</mapper>")

    @staticmethod
    def build_result_map(file_pointer: TextIO, table_info: TableInfo) -> None:
        CommentBuilder.build_xml_comment(file_pointer, "实体映射", "\t")
        file_pointer.write("\n")
        po_class = Constants.get_instance().PACKAGE_PO + "." + table_info.bean_name
        file_pointer.write(f"\t<resultMap id=\"{MapperXmlBuilder.base_result_map}\" type=\"{po_class}\">")
        file_pointer.write("\n")

        last_element = list(table_info.field_map.values())[-1]
        for i in table_info.field_map.values():
            if i.comment != "":
                CommentBuilder.build_xml_comment(file_pointer, i.comment, "\t\t")
                file_pointer.write("\n")

            # 写错了一个地方我调试了半天！field与property不同!
            if table_info.key_index_map != {} and len(table_info.key_index_map["PRIMARY"]) == 1:
                if table_info.key_index_map["PRIMARY"][0] == i:
                    file_pointer.write(f"\t\t<id column=\"{i.filed_name}\" property=\"{i.property_name}\"/>")
                    file_pointer.write("\n")
                    continue
            file_pointer.write(f"\t\t<result column=\"{i.filed_name}\" property=\"{i.property_name}\"/>")
            if i != last_element:
                file_pointer.write("\n")
        file_pointer.write("\n")
        file_pointer.write("\t</resultMap>")

    @staticmethod
    def build_mapper_head(file_pointer: TextIO, table_info: TableInfo) -> None:
        """
        构建mapper xml头部
        :param file_pointer:
        :param table_info:
        :return:
        """
        file_pointer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
        file_pointer.write(
            "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">")
        file_pointer.write("\n")
        file_name = uppercase_first_char(table_info.bean_name) + Constants.get_instance().SUFFIX_MAPPERS
        file_pointer.write(f"<mapper namespace=\"{Constants.get_instance().PACKAGE_MAPPERS}.{file_name}\">")
        file_pointer.write("\n")

    @staticmethod
    def build_common_select_column(file_pointer: TextIO, table_info: TableInfo) -> None:
        """
        构建查询列\n
        :param file_pointer:\n
        :param table_info:
        :return:
        """
        fields = list(table_info.field_map.values())
        fields_count = len(fields)
        properties = ""
        for i in range(fields_count):
            if i != fields_count - 1:
                properties = properties + fields[i].filed_name + ","
            else:
                properties = properties + fields[i].filed_name
        file_pointer.write("\n")

        CommentBuilder.build_xml_comment(file_pointer, "通用查询列", "\t")
        file_pointer.write("\n")
        file_pointer.write(f"\t<sql id=\"{MapperXmlBuilder.base_column_list}\">")
        file_pointer.write("\n")
        file_pointer.write(f"\t\t{properties}")
        file_pointer.write("\n")
        file_pointer.write("\t</sql>")
        file_pointer.write("\n")

    @staticmethod
    def build_base_select_condition(file_pointer: TextIO, table_info: TableInfo) -> None:
        """
        构建基本查询条件\n
        :param file_pointer:文件指针
        :param table_info:表信息
        """
        CommentBuilder.build_xml_comment(file_pointer, "通用查询列", "\t")
        file_pointer.write("\n")
        file_pointer.write(f"\t<sql id=\"{MapperXmlBuilder.base_query_condition}\">")
        file_pointer.write("\n")
        for i in table_info.field_map.values():
            if i.java_type == "String":
                file_pointer.write(f"\t\t<if test=\"query.{i.property_name} != null and query.{i.property_name}!=''\">")
            else:
                file_pointer.write(f"\t\t<if test=\"query.{i.property_name} != null\">")
            file_pointer.write("\n")
            file_pointer.write(f"\t\t\tand {i.property_name} = #{{query.{i.property_name}}}")
            file_pointer.write("\n")
            file_pointer.write("\t\t</if>")
            file_pointer.write("\n")
        file_pointer.write("\t</sql>")
        file_pointer.write("\n")

    @staticmethod
    def build_extend_select_condition(file_pointer: TextIO, table_info: TableInfo) -> None:
        """
        构建扩展查询条件\n
        :param file_pointer:文件指针\n
        :param table_info:表信息
        """
        CommentBuilder.build_xml_comment(file_pointer, "扩展列", "\t")
        file_pointer.write("\n")
        file_pointer.write(f"\t<sql id=\"{MapperXmlBuilder.base_query_condition_extend}\">")
        file_pointer.write("\n")
        for i in table_info.extend_fields:
            if i.java_type == "String":
                file_pointer.write(f"\t\t<if test=\"query.{i.property_name} != null and query.{i.property_name}!=''\">")
            else:
                file_pointer.write(f"\t\t<if test=\"query.{i.property_name} != null\">")
            file_pointer.write("\n")
            and_where = ""
            if i.java_type == "String":
                and_where = f"\t\t\tand {i.filed_name} like concat('%', #{{query.{i.property_name}}},'%')"
            elif i.sql_type == "date" or i.sql_type == "datetime":
                if get_last_suffix(i.property_name) == Constants.get_instance().SUFFIX_BEAN_PARAM_TIME_START:
                    and_where = f"\t\t\t<![CDATA[ and {i.filed_name} >= str_to_date(#{{query.{i.property_name}}}, '%Y-%m-%d') ]]>"
                elif get_last_suffix(i.property_name) == Constants.get_instance().SUFFIX_BEAN_PARAM_TIME_END:
                    and_where = f"\t\t\t<![CDATA[ and {i.filed_name} < date_sub(str_to_date(#{{query.{i.property_name}}},'%Y-%m-%d), interval 1 day) ]]>"
            if and_where == "":
                file_pointer.write(f"\t\t\tand {i.property_name} = #{{query.{i.property_name}}}")
            else:
                file_pointer.write(and_where)
            file_pointer.write("\n")
            file_pointer.write("\t\t</if>")
            file_pointer.write("\n")
        file_pointer.write("\t</sql>")
        file_pointer.write("\n")

    @staticmethod
    def connect_condition(file_pointer: TextIO) -> None:
        """
        连接拓展条件与基本条件
        :param file_pointer:文件指针
        """
        CommentBuilder.build_xml_comment(file_pointer, "扩展查询条件", "\t")
        file_pointer.write("\n")
        file_pointer.write(f"\t<sql id=\"{MapperXmlBuilder.query_condition}\">")
        file_pointer.write("\n")
        file_pointer.write("\t\t<where>")
        file_pointer.write("\n")
        file_pointer.write(f"\t\t\t<include refid=\"{MapperXmlBuilder.base_query_condition}\"/>")
        file_pointer.write("\n")
        file_pointer.write(f"\t\t\t<include refid=\"{MapperXmlBuilder.base_query_condition_extend}\"/>")
        file_pointer.write("\n")
        file_pointer.write("\t\t</where>")
        file_pointer.write("\n")
        file_pointer.write("\t</sql>")

    @staticmethod
    def build_select_list(file_pointer: TextIO, table_info: TableInfo) -> None:
        """
        构建扩展查询条件
        :param file_pointer:文件指针
        :param table_info:表信息
        """
        CommentBuilder.build_xml_comment(file_pointer, "查询列表", "\t")
        file_pointer.write("\n")
        file_pointer.write("\t<select id=\"selectList\" resultMap=\"base_result_map\">")
        file_pointer.write("\n")
        file_pointer.write(
            f"\t\tSELECT <include refid=\"{MapperXmlBuilder.base_column_list}\"/> FROM {table_info.table_name} <include refid=\"{MapperXmlBuilder.query_condition}\"/>")
        file_pointer.write("\n")
        file_pointer.write("\t\t<if test=\"query.orderBy != null\">order by ${query.orderBy}</if>")
        file_pointer.write("\n")
        file_pointer.write(
            "\t\t<if test=\"query.simplePage != null\">limit #{query.simplePage.start},#{query.simplePage.end}</if>")
        file_pointer.write("\n")
        file_pointer.write("\t</select>")

    @staticmethod
    def build_select_count(file_pointer: TextIO, table_info: TableInfo) -> None:
        CommentBuilder.build_xml_comment(file_pointer, "查询数量", "\t")
        file_pointer.write("\n")
        file_pointer.write("\t<select id=\"selectCount\" resultType=\"java.lang.Long\">")
        file_pointer.write("\n")
        file_pointer.write(
            f"\t\tSELECT count(1) FROM {table_info.table_name} <include refid=\"{MapperXmlBuilder.query_condition}\"/>")
        file_pointer.write("\n")
        file_pointer.write("\t</select>")

    @staticmethod
    def build_insert(file_pointer: TextIO, table_info: TableInfo, interface_name: str) -> None:
        if interface_name == "insert":
            CommentBuilder.build_xml_comment(file_pointer, "插入 (匹配有值的字段)", "\t")
        elif interface_name == "insertOrUpdate":
            CommentBuilder.build_xml_comment(file_pointer, "插入或者更新", "\t")
        else:
            raise Exception("不存在的insert方法名应该为[insertOrUpdate,insert]之一")
        file_pointer.write("\n")
        file_pointer.write(
            f"\t<insert id=\"{interface_name}\" parameterType=\"{Constants.get_instance().PACKAGE_PO}.{table_info.bean_name}\">")
        file_pointer.write("\n")

        for i in table_info.increment_lst:
            file_pointer.write(
                f"\t\t<selectKey keyProperty=\"bean.{i.property_name}\" resultType=\"{i.java_type}\" order=\"AFTER\">")
            file_pointer.write("\n")
            file_pointer.write("\t\t\tSELECT LAST_INSERT_ID()")
            file_pointer.write("\n")
            file_pointer.write("\t\t</selectKey>")
            file_pointer.write("\n")
        file_pointer.write(f"\t\tINSERT INTO {table_info.table_name}")
        file_pointer.write("\n")

        file_pointer.write("\t\t<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">")
        file_pointer.write("\n")
        for i in table_info.field_map.values():
            if i in table_info.increment_lst:
                continue
            file_pointer.write(f"\t\t\t<if test=\"bean.{i.property_name} != null\">")
            file_pointer.write("\n")
            file_pointer.write(f"\t\t\t\t{i.filed_name},")
            file_pointer.write("\n")
            file_pointer.write("\t\t\t</if>")
            file_pointer.write("\n")
        file_pointer.write("\t\t</trim>")
        file_pointer.write("\n")

        file_pointer.write("\t\t<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">")
        file_pointer.write("\n")
        for i in table_info.field_map.values():
            if i in table_info.increment_lst:
                continue
            file_pointer.write(f"\t\t\t<if test=\"bean.{i.property_name}!=null\">")
            file_pointer.write("\n")
            file_pointer.write(f"\t\t\t\t#{{bean.{i.property_name}}},")
            file_pointer.write("\n")
            file_pointer.write("\t\t\t</if>")
            file_pointer.write("\n")
        file_pointer.write("\t\t</trim>")
        file_pointer.write("\n")
        if interface_name == "insertOrUpdate":
            MapperXmlBuilder.build_insert_or_update(file_pointer, table_info)
            file_pointer.write("\n")
        file_pointer.write("\t</insert>")

    @staticmethod
    def build_insert_or_update(file_pointer: TextIO, table_info: TableInfo) -> None:
        file_pointer.write("\t\ton DUPLICATE key update")
        file_pointer.write("\n")
        file_pointer.write("\t\t<trim prefix=\"\" suffix=\"\" suffixOverrides=\",\">")
        file_pointer.write("\n")
        for i in table_info.field_map.values():
            if i in table_info.key_index_map["PRIMARY"]:
                continue
            file_pointer.write(f"\t\t\t<if test=\"bean.{i.property_name}!=null\">")
            file_pointer.write("\n")
            file_pointer.write(f"\t\t\t\t{i.filed_name} = VALUES({i.filed_name}),")
            file_pointer.write("\n")
            file_pointer.write("\t\t\t</if>")
            file_pointer.write("\n")
        file_pointer.write("\t\t</trim>")

    @staticmethod
    def build_insert_batch(file_pointer: TextIO, table_info: TableInfo, interface_name: str) -> None:
        if interface_name == "insertBatch":
            CommentBuilder.build_xml_comment(file_pointer, "批量新增修改 (批量插入)", "\t")
        elif interface_name == "insertOrUpdateBatch":
            CommentBuilder.build_xml_comment(file_pointer, "批量新增修改 (批量插入)", "\t")
        else:
            raise Exception("不存在的insert方法名应该为[insertBatchOrUpdate,insertBatch]之一")
        file_pointer.write("\n")
        file_pointer.write(
            f"\t<insert id=\"{interface_name}\" parameterType=\"{Constants.get_instance().PACKAGE_PO}.{table_info.bean_name}\">")
        file_pointer.write("\n")
        fields = ""
        properties = ""
        for i in table_info.field_map.values():
            if i != table_info.get_last_field():
                fields = fields + i.filed_name + ","
                properties = properties + f"#{{item.{i.property_name}}},"
            else:
                fields = fields + i.filed_name
                properties = properties + f"#{{item.{i.property_name}}}"

        file_pointer.write(f"\t\tINSERT INTO {table_info.table_name}({fields}) VALUES")
        file_pointer.write("\n")
        file_pointer.write("\t\t<foreach collection=\"list\" item=\"item\" separator=\",\">")
        file_pointer.write("\n")
        file_pointer.write("\t\t\t(")
        file_pointer.write("\n")
        file_pointer.write("\t\t\t\t" + properties)
        file_pointer.write("\n")
        file_pointer.write("\t\t\t)")
        file_pointer.write("\n")
        file_pointer.write("\t\t</foreach>")
        file_pointer.write("\n")
        if interface_name == "insertOrUpdateBatch":
            file_pointer.write("\t\ton DUPLICATE key update")
            file_pointer.write("\n")
            for i in table_info.field_map.values():
                if i != table_info.get_last_field():
                    file_pointer.write(f"\t\t{i.filed_name} = VALUES({i.filed_name}),")
                else:
                    file_pointer.write(f"\t\t{i.filed_name} = VALUES({i.filed_name})")
                file_pointer.write("\n")
        file_pointer.write("\t</insert>")

    @staticmethod
    def build_fields_curd(file_pointer: TextIO, table_info: TableInfo) -> None:
        for i in table_info.field_map.values():
            CommentBuilder.build_xml_comment(file_pointer, f"根据{i.property_name}查询", "\t")
            file_pointer.write("\n")
            file_pointer.write(
                f"\t<select id=\"selectBy{uppercase_first_char(i.property_name)}\" resultMap=\"{MapperXmlBuilder.base_result_map}\">")
            file_pointer.write("\n")
            file_pointer.write(
                f"\t\tselect <include refid=\"{MapperXmlBuilder.base_column_list}\"/> from {table_info.table_name} where {i.filed_name} = #{{{i.property_name}}}")
            file_pointer.write("\n")
            file_pointer.write("\t</select>")
            file_pointer.write("\n")
            file_pointer.write("\n")

            CommentBuilder.build_xml_comment(file_pointer, f"根据{i.property_name}删除", "\t")
            file_pointer.write("\n")
            file_pointer.write(f"\t<delete id=\"deleteBy{uppercase_first_char(i.property_name)}\">")
            file_pointer.write("\n")
            file_pointer.write(f"\t\tdelete from {table_info.table_name} where {i.filed_name} = #{{{i.property_name}}}")
            file_pointer.write("\n")
            file_pointer.write("\t</delete>")
            file_pointer.write("\n")
            file_pointer.write("\n")

            CommentBuilder.build_xml_comment(file_pointer, f"根据{i.property_name}更新", "\t")
            file_pointer.write("\n")
            file_pointer.write(f"\t<update id=\"updateBy{uppercase_first_char(i.property_name)}\" parameterType=\"{Constants.get_instance().PACKAGE_PO}.{table_info.bean_name}\">")

            file_pointer.write("\n")
            file_pointer.write(f"\t\tupdate {table_info.table_name}")
            file_pointer.write("\n")
            file_pointer.write("\t\t<set>")
            for j in table_info.field_map.values():
                if j == table_info.key_index_map["PRIMARY"][0]:
                    continue
                file_pointer.write("\n")
                file_pointer.write(f"\t\t\t<if test=\"bean.{j.property_name}!=null\">")
                file_pointer.write("\n")
                file_pointer.write(f"\t\t\t\t{j.filed_name} = #{{bean.{j.property_name}}},")
                file_pointer.write("\n")
                file_pointer.write("\t\t\t</if>")
                file_pointer.write("\n")
            file_pointer.write("\t\t</set>")
            file_pointer.write("\n")
            file_pointer.write(f"\twhere {i.filed_name} = #{{{i.property_name}}}")
            file_pointer.write("\n")
            file_pointer.write("\t</update>")
            file_pointer.write("\n")